/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2008，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	OptionWriter.java
 * 模块说明：	
 * 修改历史：
 * Mar 6, 2008 - lxm - 创建。
 */
package com.hd123.rumba.option.biz;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Transaction;

import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.option.COptionalClassInfo;
import com.hd123.rumba.option.IOptionCollection;
import com.hd123.rumba.option.IOptionItem;
import com.hd123.rumba.option.IOptionMgr;
import com.hd123.rumba.option.IOptionalClassInfo;
import com.hd123.rumba.option.IOptionalClassInfoMgr;
import com.hd123.rumba.runtime.IManager;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.OperateContext;
import com.hd123.rumba.runtime.biz.BManager;
import com.hd123.rumba.user.CUser;
import com.hd123.rumba.user.IUser;
import com.hd123.rumba.util.ObjectUtil;

/**
 * 选项写入器代理
 * <p>
 * 
 * 在很多情况下，不同选项之间的取值不存在完整性问题；或者在某些情况下，取值完整性将由调用者负责。在这两种情况下，设计者推荐采用该类。但如果你所要解决的问题中选项的完整性非常重要，则设计者推荐采用功能更强的{@link IBOptionMgr}和{@link BOptionCollectioin}。
 * <p>
 * 
 * 需要通过静态方法{@link #getWriter}创建写入器实例，需要指定域、选项类和用户（一般为当前登录用户）。
 * 
 * @author lxm
 * 
 */
public class OptionWriter extends BManager {

  @Override
  public IManager getPerzMgr() throws BusinessException {
    return getOptMgr();
  }

  private OptionWriter() {
    super();
  }

  /**
   * 取得一个选项写入器。
   * <p>
   * 
   * 在某些情况下指定的选项被禁止修改，将会抛出一个异常。可能的原因如下：
   * <ol>
   * <li>选项设置个性化级别不够。
   * <li>选项已经被锁定禁止修改。
   * </ol>
   * 
   * @param domain
   *          指定所属域。
   * @param optionalClass
   *          指定选项类。
   * @param user
   *          指定所操纵的选购值所属用户，一般传入当前登录用户。允许传入null，表示将操作的是全局取值。
   * @return 返回创建的选项写入器。
   * @throws BusinessException
   * @throws HibernateException
   */
  public static OptionWriter getWriter(IDomain domain, Class optionalClass, IUser user)
      throws BusinessException, HibernateException {
    ObjectUtil.checkParameterNotNull(domain, "domain");
    ObjectUtil.checkParameterNotNull(optionalClass, "optionalClass");

    OptionWriter writer = new OptionWriter();
    writer.setDomain(domain);
    writer.setOptionalClass(optionalClass);
    writer.setUser(user);
    writer.validate();
    return writer;
  }

  /**
   * 写入选项取值。
   * 
   * @param name
   *          指定的选项名。
   * @param value
   *          指定被写入的选项取值，要求对象必须是可序列化的。
   * @param operate
   * @throws BusinessException
   * @throws HibernateException
   */
  public void writeValue(String name, Object value, OperateContext operate)
      throws BusinessException, HibernateException {
    ObjectUtil.checkParameterNotNull(name, "name");
    ObjectUtil.checkParameterNotNull(value, "value");
    doWriteValue(name, value, operate);
  }

  /**
   * 写入选项取值（含事务）。
   * 
   * @param name
   *          指定的选项名。
   * @param value
   *          指定被写入的选项取值，要求对象必须是可序列化的。
   * @param operate
   * @throws BusinessException
   * @throws HibernateException
   */
  public void writeValueT(String name, Object value, OperateContext operate)
      throws BusinessException, HibernateException {
    ObjectUtil.checkParameterNotNull(name, "name");
    ObjectUtil.checkParameterNotNull(value, "value");

    Transaction tx = getSession().beginTransaction();
    try {
      doWriteValue(name, value, operate);
      tx.commit();
    } catch (HibernateException e) {
      tx.rollback();
      throw e;
    } catch (BusinessException e) {
      tx.rollback();
      throw e;
    } finally {
      getSession().flush();
    }
  }

  /**
   * 删除选项取值。
   * 
   * @param name
   *          指定选项名。
   * @param operate
   * @throws BusinessException
   * @throws HibernateException
   */
  public void removeValue(String name, OperateContext operate) throws BusinessException,
      HibernateException {
    ObjectUtil.checkParameterNotNull(name, "name");
    doRemoveValue(name, operate);
  }

  /**
   * 删除选项取值（含事务）。
   * 
   * @param name
   *          指定选项名。
   * @param operate
   * @throws BusinessException
   * @throws HibernateException
   */
  public void removeValueT(String name, OperateContext operate) throws BusinessException,
      HibernateException {
    ObjectUtil.checkParameterNotNull(name, "name");

    Transaction tx = getSession().beginTransaction();
    try {
      doRemoveValue(name, operate);
      tx.commit();
    } catch (HibernateException e) {
      tx.rollback();
      throw e;
    } catch (BusinessException e) {
      tx.rollback();
      throw e;
    } finally {
      getSession().flush();
    }
  }

  /**
   * 取得对应的选项阅读器。
   * 
   * @return
   * @throws BusinessException
   * @see OptionReader#getReader
   */
  public OptionReader getReader() throws BusinessException {
    return OptionReader.getReader(domain, optionalClass, user);
  }

  private IDomain domain = null;
  private Class optionalClass = null;
  private IUser user = null;

  /** 属性：所属域 */
  public IDomain getDomain() {
    return domain;
  }

  private void setDomain(IDomain domain) {
    this.domain = domain;
  }

  /** 属性：所指向的选项类。 */
  public Class getOptionalClass() {
    return optionalClass;
  }

  private void setOptionalClass(Class optionalClass) {
    this.optionalClass = optionalClass;
  }

  /** 属性：所属用户 */
  public IUser getUser() {
    return user;
  }

  private void setUser(IUser user) {
    this.user = user;
  }

  private IOptionMgr optMgr = null;
  private IOptionalClassInfoMgr optInfoMgr = null;

  private IOptionMgr getOptMgr() throws BusinessException {
    if (optMgr == null)
      optMgr = (IOptionMgr) NamingMgr.getInstance().lookupMgr(IOptionMgr.class);
    return optMgr;
  }

  private IOptionalClassInfoMgr getOptInfoMgr() throws BusinessException {
    if (optInfoMgr == null)
      optInfoMgr = (IOptionalClassInfoMgr) NamingMgr.getInstance().lookupMgr(
          IOptionalClassInfoMgr.class);
    return optInfoMgr;
  }

  private void validate() throws BusinessException, HibernateException {
    // 个性化级别的检查
    if (user != null) {
      IOptionalClassInfo classInfo = getOptInfoMgr().fetchClassInfoByClass(optionalClass.getName());
      if (COptionalClassInfo.VALUE_INDIVIDUATION_DOMAIN.equals(classInfo.getIndividuation()))
        throw new BusinessException("选项禁止" + CUser.CAPTION_CLASS + "个性化设置("
            + optionalClass.getName() + ")。");
    }

    // 检查是否被系统管理员锁定
    if (user != null) {
      IOptionCollection globalCol = getOptMgr().getCollectionByClass(domain, optionalClass, null);
      if (globalCol == null)
        globalCol = getOptMgr().createCollection(domain, optionalClass, null);
      if (globalCol.getLocked().booleanValue())
        throw new BusinessException(CUser.CAPTION_CLASS + "个性化设置功能已经被系统管理员锁定，禁止修改。");
    }
  }

  private void doWriteValue(String name, Object value, OperateContext operate)
      throws BusinessException, HibernateException {
    validate();
    
    IOptionCollection col = this.createOrLoadCollection();
    this.readyToWrite(col);

    IOptionItem item = col.getItemByName(name);
    if (item == null) {
      item = getOptMgr().createItem(col);
      item.setName(name);
    }
    item.setValue(value);
    getOptMgr().addItemToCollection(col, item);

    getOptMgr().saveCollection(col, operate);
  }

  private void doRemoveValue(String name, OperateContext operate) throws BusinessException,
      HibernateException {
    validate();
    
    IOptionCollection col = this.createOrLoadCollection();
    this.readyToWrite(col);

    IOptionItem item = col.getItemByName(name);
    if (item == null)
      return;

    getOptMgr().removeItemFromCollection(col, item);

    getOptMgr().saveCollection(col, operate);
  }

  private IOptionCollection createOrLoadCollection() throws BusinessException, HibernateException {
    IOptionCollection col = getOptMgr().getCollectionByClass(domain, optionalClass, user);
    if (col == null)
      col = getOptMgr().createCollection(domain, optionalClass, user);
    return col;
  }

  private void readyToWrite(IOptionCollection col) throws BusinessException, HibernateException {
    if (user == null)
      return;
    
    // 若当前取值为继承的，则改为非继承，并从全局设置复制所有取值。
    if (!col.getInherited().booleanValue())
      return;
    col.setInherited(Boolean.FALSE);
    IOptionCollection gblCol = getOptMgr().getCollectionByClass(domain, optionalClass, null);
    getOptMgr().copyItems(gblCol, col);
  }

}
